package 多线程;

import java.util.concurrent.locks.ReentrantLock;

/*
    解决线程安全问题的方式二:
        Lock(锁)
             从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步,同步锁使用Lock对象充当
             java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
             ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁

    synchronized与Lock的对比:
         Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
         Lock只有代码块锁,synchronized有代码块锁和方法锁
         使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)

    优先使用顺序: Lock --> 同步代码块(已经进入了方法体,分配了相应资源) --> 同步方法(在方法体之外)
 */
public class ThreadTest11 {

    public static void main(String[] args) {
        Window window = new Window();

        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);

        t1.setName("线程一");
        t2.setName("线程二");
        t3.setName("线程三");

        t1.start();
        t2.start();
        t3.start();
    }

}

//使用Lock解决线程安全问题
class Window implements Runnable{

    private int ticket = 100;

    //实例化Lock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                //调用锁定方法lock()
                this.lock.lock();

                if(ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + ticket--);
                }else {
                    break;
                }
            }finally {
                //调用解锁方法unlock()
                this.lock.unlock();
            }

        }
    }
}
